/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

package com.huawei.api.cmd;

import com.huawei.api.bean.CmdExecResult;
import com.huawei.api.bean.CommandInfo;
import com.huawei.api.exception.InterruptException;
import com.huawei.api.utils.PathUtil;
import lombok.Data;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * 工具命令执行器
 *
 * @since 2023/10/28
 */
@Data
public class ToolRunner extends BaseRunner {
    private static final Logger LOGGER = LoggerFactory.getLogger(ToolRunner.class);

    public List<String> errorMsg;

    public int step;

    public int total;

    public ToolRunner(CommandInfo commandInfo, int timeout, boolean isBlock, int step, int total) {
        this.commandInfo = commandInfo;
        this.timeout = timeout;
        this.isBlock = isBlock;
        this.step = step;
        this.total = total;
        this.errorMsg = new ArrayList<>();
    }

    /**
     * 执行命令
     *
     * @return 结果
     * @throws InterruptException 异常
     */
    @Override
    public CmdExecResult run() throws InterruptException {
        try {
            if (null == commandInfo || null == commandInfo.getCommandLine()
                    || StringUtils.isBlank(commandInfo.getCommandLine().toString())) {
                errorMsg.add("command line is empty");
                return null;
            }
            LOGGER.info(PathUtil.getSystemPath()  +"   cmd is {}", commandInfo.getCommandLine().toString());
            runCmd(commandInfo.getCommandLine());
            CmdExecResult result = new CmdExecResult();
            result.setOutMsgLines(getMsgFromStream(cmdRunner.getOutStream(), StandardCharsets.UTF_8));
            result.setErrMsgLines(getMsgFromStream(cmdRunner.getErrStream(), StandardCharsets.UTF_8));
            result.setExpectResultCode(commandInfo.getExpectCode());
            result.setActualResultCode(cmdRunner.getResultHandler().getExitValue());
            LOGGER.info("Expect exit code: {},Actual exit code: {}, msg is:{} , error  msg: {}", result.getExpectResultCode(),
                    result.getActualResultCode(), result.getOutMsgLines().toString(), result.getErrMsgLines());
            String progressRate = step + "/" + total;
            LOGGER.info("exec cmdline process is: {},cmd is {}", progressRate, commandInfo.getCommandLine().toString());
            result.setReportFilePath(commandInfo.getReportFilePath());
            return result;
        } catch (InterruptException e) {
            LOGGER.error("run cmd: {}, error is {}", commandInfo.getCommandLine().toString(), e.getMessage());
            throw new InterruptException(e.getMessage());
        }
    }

    /**
     * 执行命令
     *
     * @param cmdLine 命令行
     * @throws InterruptException 异常
     */
    public void runCmd(CommandLine cmdLine) throws InterruptException {
        cmdRunner.reset();
        try {
            cmdRunner.run(cmdLine, timeout, isBlock);
        } catch (Exception exception) {
            throw new InterruptException("run cmd: " + cmdLine + " error", exception);
        }
    }

    /**
     * 从结果中返回
     *
     * @param outputStream 输出流
     * @param charset      charset
     * @return 信息
     */
    private List<String> getMsgFromStream(ByteArrayOutputStream outputStream, Charset charset) {
        List<String> msgLines = new ArrayList<>();

        String line;
        try (InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
             BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset))) {
            while ((line = reader.readLine()) != null) {
                msgLines.add(line);
            }
        } catch (IOException e) {
            throw new InterruptException(e.getMessage());
        }
        return msgLines;
    }

    /**
     * 异常信息
     *
     * @return 异常信息
     */
    public List<String> getErrorMsg() {
        return errorMsg;
    }
}
